﻿#region

using System.Reflection;
using Microsoft.Extensions.Logging;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Listener;
using Vin.Extension.Quartz.Models;
using Vin.Tool.AspNetCore.Service;
using Vin.Tool.Attributes;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.JsonCategory;
using Vin.Tool.Enums;

#endregion

namespace Vin.Extension.Quartz.Service;

/// <summary>
/// 只会运行期间有效，在程序关闭后任务会失效，无法持久化任务
/// </summary>
[Service(Lifetime = Lifetime.Singleton)]
public class VinTaskService : VinBaseService
{
    private readonly IScheduler _scheduler;

    public VinTaskService(ISchedulerFactory schedulerFactory)
    {
        _scheduler = schedulerFactory.GetScheduler().Result;
    }

    #region 调度器

    /// <summary>
    /// 启动调度器
    /// </summary>
    public async Task StartScheduler()
    {
        if (_scheduler.IsStarted)
        {
            // _logger.LogInformation("计划任务已经启动");
            return;
        }

        await _scheduler.Start();
        Logger.LogInformation("计划任务启动成功");
    }

    /// <summary>
    /// 停止调度器
    /// </summary>
    public async Task StopScheduler()
    {
        if (_scheduler.IsShutdown)
        {
            // _logger.LogInformation("计划任务已停止");
            return;
        }

        await _scheduler.Shutdown();
        Logger.LogInformation("计划任务停止成功");
    }

    /// <summary>
    /// 获取调度器
    /// </summary>
    public IScheduler GetScheduler => _scheduler;

    #endregion

    #region 任务列表

    public async Task TaskList()
    {
        var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup());
        Console.WriteLine("任务列表：" + jobKeys.Count);
        foreach (var jobKey in jobKeys)
        {
            Console.WriteLine(jobKey);
            // var jobDetail = await _scheduler.GetJobDetail(jobKey);
        }
    }

    #endregion

    #region 创建任务

    /// <summary>
    /// 创建任务
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <returns></returns>
    public IJobDetail? CreateJob(TaskInfo taskInfo)
    {
        JobBuilder jobBuilder = null!;

        // 根据任务类型创建任务
        switch (taskInfo.TaskInfoType)
        {
            case TaskInfoType.Generics:
                jobBuilder = JobBuilder.Create(taskInfo.JobType!);
                break;
            case TaskInfoType.Assembly:
                var assembly = Assembly.LoadFile(taskInfo.AssemblyPath!);
                var allName = taskInfo.ClassName!.StartsWith(taskInfo.AssemblyName!)
                    ? taskInfo.ClassName
                    : taskInfo.AssemblyName + "." + taskInfo.ClassName;
                var jobType = assembly.GetType(allName);
                if (jobType == null)
                {
                    Logger.LogInformation(
                        "未找到任务类，请检查程序集名称（{TaskInfoAssemblyName}）和类名（{TaskInfoClassName}）是否正确！（{AllName}）",
                        taskInfo.AssemblyName, taskInfo.ClassName, allName);
                    return null;
                }
                
                jobBuilder = JobBuilder.Create(jobType);
                break;
        }

        // 设置任务名称和分组
        jobBuilder = jobBuilder.WithIdentity(GetJobKey(taskInfo));

        // 设置任务参数
        if (taskInfo.Parameters?.Count > 0)
        {
            jobBuilder.UsingJobData(new JobDataMap(taskInfo.Parameters));
            // foreach (var (key, value) in taskInfo.Parameters)
            // {
            //     jobBuilder = jobBuilder.UsingJobData(key, value);
            // }
        }

        // 构建任务并返回
        return jobBuilder.Build();
    }

    #endregion

    #region 创建触发器

    /// <summary>
    /// 创建简单的触发器
    /// </summary>
    private TriggerBuilder CreateSimpleTrigger(TaskInfo taskInfo, TriggerBuilder builder)
    {
        builder = builder.WithSimpleSchedule((options) =>
        {
            // 循环
            switch (taskInfo.LoopType)
            {
                case LoopType.Finite:
                    options.WithRepeatCount(taskInfo.LoopCount);
                    break;
                case LoopType.Infinite:
                    options.RepeatForever();
                    break;
            }

            // 间隔
            switch (taskInfo.IntervalType)
            {
                case IntervalType.Second:
                    options.WithIntervalInSeconds(taskInfo.Interval);
                    break;
                case IntervalType.Minute:
                    options.WithIntervalInMinutes(taskInfo.Interval);
                    break;
                case IntervalType.Hour:
                    options.WithIntervalInHours(taskInfo.Interval);
                    break;
                case IntervalType.Day:
                    options.WithIntervalInHours(taskInfo.Interval * 24);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        });

        return builder;
    }

    /// <summary>
    /// 创建Cron表达式的触发器
    /// </summary>
    private TriggerBuilder CreateCronTrigger(TaskInfo taskInfo, TriggerBuilder builder)
    {
        if (!taskInfo.CronExpression.IsNullOrEmpty()) builder = builder.WithCronSchedule(taskInfo.CronExpression!);
        return builder;
    }

    /// <summary>
    /// 创建触发器
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <returns></returns>
    public ITrigger CreateTrigger(TaskInfo taskInfo)
    {
        var builder = TriggerBuilder.Create();
        builder = taskInfo.JobGroup.IsNullOrEmpty()
            ? builder.WithIdentity(taskInfo.JobId)
            : builder.WithIdentity(taskInfo.JobId, taskInfo.JobGroup!);
        // 开始时间
        if (taskInfo.StartTime.HasValue) builder = builder.StartAt(new DateTimeOffset(taskInfo.StartTime.Value));
        // 结束时间
        if (taskInfo.EndTime.HasValue) builder = builder.EndAt(new DateTimeOffset(taskInfo.EndTime.Value));
        // 是否立即执行
        if (taskInfo.IsStartNow == true) builder = builder.StartNow();
        // if (!taskInfo.StartTime.HasValue || DateTime.Now > taskInfo.StartTime.Value) builder = builder.StartNow();

        // 根据触发器类型创建触发器
        builder = taskInfo.TriggerType switch
        {
            TriggerType.Simple => CreateSimpleTrigger(taskInfo, builder),
            TriggerType.Cron => CreateCronTrigger(taskInfo, builder),
            _ => throw new ArgumentOutOfRangeException()
        };

        // 绑定任务
        builder = builder.ForJob(GetJobKey(taskInfo));

        return builder.Build();
    }

    #endregion

    #region 定时任务

    #region 获取JobKey

    private JobKey GetJobKey(TaskInfo taskInfo)
    {
        return GetJobKey(taskInfo.JobId, taskInfo.JobGroup);
    }

    private JobKey GetJobKey(string jobId, string? jobGroup = null)
    {
        return jobGroup.IsNullOrEmpty() ? new JobKey(jobId) : new JobKey(jobId, jobGroup);
    }

    #endregion

    #region 添加

    /// <summary>
    /// 添加定时任务（如果已经存在相同任务id的则更新）
    /// </summary>
    public async Task<bool> AddJobTask<T>(TaskInfo taskInfo) where T : IJob
    {
        taskInfo.JobType = typeof(T);
        return await AddJobTask(taskInfo);
    }

    /// <summary>
    /// 检验任务信息
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <exception cref="Exception"></exception>
    private void CheckTaskInfo(TaskInfo taskInfo)
    {
        // 检验类型
        switch (taskInfo.TaskInfoType)
        {
            case TaskInfoType.Generics:
                if (taskInfo.JobType == null)
                {
                    throw new Exception("创建定时任务失败！执行的任务类型不可以为空！或选择Assembly类型！或使用泛型指定模式");
                }

                break;
            case TaskInfoType.Assembly:
                if (taskInfo.AssemblyName.IsNullOrEmpty() || taskInfo.ClassName.IsNullOrEmpty())
                {
                    throw new Exception("创建定时任务失败！当任务类型为Assembly时，程序集名称和类名不可以为空！或选择Generics泛型类型！");
                }

                break;
        }
    }

    /// <summary>
    /// 添加定时任务（如果已经存在相同任务id的则更新）
    /// </summary>
    public async Task<bool> AddJobTask(TaskInfo taskInfo)
    {
        // 检验任务信息
        CheckTaskInfo(taskInfo);

        // 启动调度器
        await StartScheduler();

        // 判断任务是否存在
        if (await QueryJobTaskIsExits(taskInfo))
        {
            await RemoveJobTask(taskInfo);
        }

        // 创建作业和触发器
        var job = CreateJob(taskInfo);
        if (job == null) throw new Exception("创建作业失败！请检查任务配置和查看日志信息！");
        var trigger = CreateTrigger(taskInfo);

        // 添加作业和触发器到调度器
        await _scheduler.ScheduleJob(job, trigger);

        return true;
    }

    #endregion

    #region 删除

    /// <summary>
    /// 删除定时任务
    /// </summary>
    /// <param name="jobId"></param>
    /// <param name="jobGroup"></param>
    /// <returns></returns>
    public async Task<bool> RemoveJobTask(string jobId, string? jobGroup = null)
    {
        return await RemoveJobTask(GetJobKey(jobId, jobGroup));
    }

    /// <summary>
    /// 删除定时任务
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <returns></returns>
    public async Task<bool> RemoveJobTask(TaskInfo taskInfo)
    {
        return await RemoveJobTask(GetJobKey(taskInfo));
    }

    /// <summary>
    /// 删除定时任务
    /// </summary>
    /// <param name="jobKey"></param>
    /// <returns></returns>
    public async Task<bool> RemoveJobTask(JobKey jobKey)
    {
        try
        {
            return await _scheduler.DeleteJob(jobKey);
        }
        catch (Exception ex)
        {
            Logger.LogError("删除定时任务失败，jobKey:{JobKey},ex:{Ex}", jobKey, ex.ToCamelCaseJson());
            return false;
        }
    }

    #endregion

    #region 查询

    /// <summary>
    /// 判断任务是否存在
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <returns></returns>
    public async Task<bool> QueryJobTaskIsExits(TaskInfo taskInfo)
    {
        return await _scheduler.CheckExists(GetJobKey(taskInfo));
    }

    /// <summary>
    /// 判断任务是否存在
    /// </summary>
    /// <param name="jobId"></param>
    /// <param name="jobGroup"></param>
    /// <returns></returns>
    public async Task<bool> QueryJobTaskIsExits(string jobId, string? jobGroup = null)
    {
        return await _scheduler.CheckExists(GetJobKey(jobId, jobGroup));
    }

    #endregion

    #region 修改

    /// <summary>
    /// 暂停定时任务
    /// </summary>
    /// <param name="taskInfo"></param>
    public async Task<bool> PauseJobTask(TaskInfo taskInfo)
    {
        return await PauseJobTask(taskInfo.JobId, taskInfo.JobGroup);
    }

    public async Task<bool> PauseJobTask(string jobId, string? jobGroup = null)
    {
        if (await QueryJobTaskIsExits(jobId, jobGroup))
        {
            await _scheduler.PauseJob(GetJobKey(jobId, jobGroup));
            return true;
        }

        return false;
    }

    /// <summary>
    /// 恢复定时任务
    /// </summary>
    /// <param name="taskInfo"></param>
    /// <returns></returns>
    public async Task<bool> ResumeJobTask(TaskInfo taskInfo)
    {
        return await ResumeJobTask(taskInfo.JobId, taskInfo.JobGroup);
    }

    public async Task<bool> ResumeJobTask(string jobId, string? jobGroup = null)
    {
        if (await QueryJobTaskIsExits(jobId, jobGroup))
        {
            await _scheduler.ResumeJob(GetJobKey(jobId, jobGroup));
            return true;
        }

        return false;
    }

    #endregion

    #endregion
}